<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
// ------------------------------------------------------------------------

/**
 * Exceptions Class
 */
class CI_Exceptions {
    var $action;
    var $severity;
    var $message;
    var $filename;
    var $line;

    /**
     * Nesting level of the output buffering mechanism
     */
    var $ob_level;

    /**
     * List if available error levels
     */
    var $levels = array(
        E_ERROR    => 'Error',
        E_WARNING   => 'Warning',
        E_PARSE    => 'Parsing Error',
        E_NOTICE   => 'Notice',
        E_CORE_ERROR  => 'Core Error',
        E_CORE_WARNING  => 'Core Warning',
        E_COMPILE_ERROR  => 'Compile Error',
        E_COMPILE_WARNING => 'Compile Warning',
        E_USER_ERROR  => 'User Error',
        E_USER_WARNING  => 'User Warning',
        E_USER_NOTICE  => 'User Notice',
        E_STRICT   => 'Runtime Notice'
    );


    /**
     * Constructor
     */
    public function __construct()
    {
        $this->ob_level = ob_get_level();
        // Note:  Do not log messages from this constructor.
    }

    // --------------------------------------------------------------------

    /**
     * Exception Logger
     */
    function log_exception($severity, $message, $filepath, $line)
    {
        $severity = ( ! isset($this->levels[$severity])) ? $severity : $this->levels[$severity];

        log_message('error', 'Severity: '.$severity.'  --> '.$message. ' '.$filepath.' '.$line, TRUE);
    }

    // --------------------------------------------------------------------

    /**
     * 404 Page Not Found Handler
     */
    function show_404($page = '', $log_error = TRUE)
    {
        $heading = "404 Page Not Found";
        $message = "The page you requested was not found.";

        // By default we log this, but allow a dev to skip it
        if ($log_error)
        {
            log_message('error', '404 Page Not Found --> '.$page);
        }

        echo $this->show_error($heading, $message, 'error_404', 404);
        exit;
    }

    // --------------------------------------------------------------------

    /**
     * 先解释一下show_php_error,show_error,和show_404之间的关系和区别。
     * show_php_error()是代码本身的一些错误，例如变量未定义之类的，平时我们调试的时候经常见到的一些错误，是不小心写错代码而导致的。
     * show_error()是有意识触发的错误，不是代码写错，而是代码不当，或者用户操作不当，比如找不到控制器，指定方法之类的，CI就show一
     * 个错误出来，当然开发者也可以调用此方法响应一个错误信息，某种程度上类似于catch到一个exception之后的处理，然后根据exception
     * 发出不同的提示信息。
     * show_404()是show_error()中的一种特殊情况，就是请求不存在的情况，响应一个404错误。
     */
    /**
     * General Error Page
     */
    function show_error($heading, $message, $template = 'error_general', $status_code = 500)
    {
        //默认是500，内部服务错误。是指由于程序代码写得不恰当而引起的，因此向浏览器回应一个内部错误。
        set_status_header($status_code);

        $message = '<p>'.implode('</p><p>', ( ! is_array($message)) ? array($message) : $message).'</p>';

        //此处的作用与show_php_error()中的相应位置作用一样。
        if (ob_get_level() > $this->ob_level + 1)
        {
            ob_end_flush();
        }
        ob_start();
        include(APPPATH.'errors/'.$template.'.php');
        $buffer = ob_get_contents();
        ob_end_clean();

        //这里是return，因为一般情况下，是使用core/Common.php中，全局函数show_error()间接使用当前Exception::show_error()方法。
        return $buffer;
    }

    // --------------------------------------------------------------------

    /**
     * Native PHP error handler
     */
    function show_php_error($severity, $message, $filepath, $line)
    {
        //取得对应错误级别相对的说明。在$this->levels中定义。
        $severity = ( ! isset($this->levels[$severity])) ? $severity : $this->levels[$severity];

        $filepath = str_replace("\\", "/", $filepath);

        //为了安全起见，只显示错误文件最后两段路径信息。
        if (FALSE !== strpos($filepath, '/'))
        {
            $x = explode('/', $filepath);
            $filepath = $x[count($x)-2].'/'.end($x);
        }


        /*
         * 如果还没看过core/Loader.php，下面这个判断可能让人有点迷惑。
         * ob_get_level()是取得当前缓冲机制的嵌套级别。（缓冲是可以一层嵌一层的。）
         * 右边的$this->ob_level是在__construct()里面同样通过ob_get_level()被赋值的。
         * 也就是说，有可能出现：Exception组件被加载时（也就是应用刚开始运行时）的缓冲级别（其实也就是程序最开始的时候的缓冲级别，那时
         * 候是还没有ob_start()过的），与发生错误的时候的缓冲级别相差1。
         * 在控制器执行$this->load->view("xxx");的时候，实质，Loader引入并执行这个视图文件的时候，是先把缓冲打开，即
         * 先ob_start()，所有输出放到缓冲区（详见：core/Loader.php中的_ci_load()）,然后再由Output处理输出。
         * 因此，如果是在视图文件发生错误，则就会出现缓冲级别相差1的情况，此时先把输出的内容给flush出来，然后再把错误信息输出。
         *
         */
        if (ob_get_level() > $this->ob_level + 1)
        {
            ob_end_flush();
        }
        ob_start();
        //错误信息模板，位于应用目录errors/下。
        include(APPPATH.'errors/error_php.php');
        $buffer = ob_get_contents();
        ob_end_clean();
        echo $buffer;
    }
}